using System;
using System.Linq;
using Godot;

namespace Arcane.Source.UI
{
    [GlobalClass]
    public partial class ScrollListContainer : RecyclerContainer
    {
        private int _visibleCount;
        [Export]
        public int VisibleCount
        {
            get => _visibleCount;
            set
            {
                _visibleCount = value;
                _height = GetRect().Size.Y / _visibleCount;
            }
        }

        private float _height;

        private float _offset = 0;
        public float Offset
        {
            get => _offset;
            set
            {
                if (Adapter == null)
                {
                    _offset = Mathf.Clamp(value, 0, float.MaxValue);
                    return;
                }

                var offset = Mathf.Clamp(value, 0, float.MaxValue);
                var delta = _offset - offset;
                _offset = offset;
                var height = GetRect().Size.Y / _visibleCount;

                var begin = Mathf.FloorToInt(_offset / height);
                if (begin != _cachedBegin)
                {
                    UpdateElements();
                }
                else
                {
                    foreach (var child in GetChildren().Cast<Control>())
                    {
                        child.Position += new Vector2(0, delta);
                    }
                }
            }
        }

        private int _cachedBegin;

        public override void _Notification(int what)
        {
            if (Adapter == null) return;

            if (what == NotificationResized)
            {
                UpdateElements();
            }

            if (what == NotificationChildOrderChanged)
            {
                FitChildren();
            }
        }

        public override void _GuiInput(InputEvent e)
        {
            if (e is InputEventMouseButton mbe)
            {
                if (mbe.ButtonIndex == MouseButton.WheelDown)
                {
                    Offset += 0.1f * _height;
                }
                else if (mbe.ButtonIndex == MouseButton.WheelUp)
                {
                    Offset -= 0.1f * _height;
                } 
            }
        }

        protected override void UpdateElements()
        {
            var rect = GetRect();
            var width = rect.Size.X;
            _height = rect.Size.Y / _visibleCount;
            var begin = Mathf.FloorToInt(_offset / _height);
            var yBegin = begin * _height - _offset;

            foreach (var child in GetChildren()) Recycle(child);

            for (int i = 0; i <= _visibleCount + 1; i++)
            {
                var child = Obtain(begin + i);
                AddChild(child);
                FitChildInRect(child, new(0, yBegin + i * _height, new(width, _height)));
            }
            _cachedBegin = begin;
        }

        protected void FitChildren()
        {
            var rect = GetRect();
            var width = rect.Size.X;
            _height = rect.Size.Y / _visibleCount;
            var begin = Mathf.FloorToInt(_offset / _height);
            var yBegin = begin * _height - _offset;

            var i = 0;
            foreach (var child in GetChildren().Cast<Control>())
            {
                Adapter.BindElement(child, begin + i);
                FitChildInRect(child, new(0, yBegin + i * _height, new(width, _height)));
                i += 1;
            }
        }
    }
}